﻿using System;
using System.Linq;
using System.Collections.Generic;

using Microsoft.Xna.Framework;

using Ah.Testudin.Engine.State;
using Ah.Testudin.Engine.Events;
using Ah.Testudin.Engine.Physics;
using Ah.Testudin.Engine.Processes;
using Ah.Testudin.Engine.Actors.Spawning;
using Ah.Testudin.Engine.Graphics.Scenes;

namespace Ah.Testudin.Engine
{
    public class GameStateMachine : IGameStateMachine
    {
        #region Private Member Variables

        private IApplicationState _mainMenuApplicationState;

        private IApplicationState _inGameState;

        private IApplicationState _pausedState;

        private IApplicationState _gameOverApplicationState;

        private IApplicationState _currentState;

        #endregion

        #region Constructors

        public GameStateMachine(
            int viewPortWidth,
            int viewPortHeight,
            IEventManager eventManager,
            IPhysicsSimulator physicsSimulator,
            IProcessManager processManager,
            IActorSpawner actorSpawner)
        {
            if (eventManager == null)
            {
                throw new ArgumentNullException("eventManager");
            }

            if (physicsSimulator == null)
            {
                throw new ArgumentNullException("physicsSimulator");
            }

            if (processManager == null)
            {
                throw new ArgumentNullException("processManager");
            }

            if (actorSpawner == null)
            {
                throw new ArgumentNullException("actorSpawner");
            }

            eventManager.RegisterEventHandler(
                EventType.MainMenu, 
                HandleMainMenuEvent);

            eventManager.RegisterEventHandler(
                EventType.GameStarted,
                HandleNewGameStarted);

            eventManager.RegisterEventHandler(
                EventType.PlayerDied,
                HandlePlayerDiedEvent);

            eventManager.RegisterEventHandler(
                EventType.GamePaused,
                HandleGamePausedEvent);

            eventManager.RegisterEventHandler(
                EventType.GameResumed, 
                HandleGameResumedEvent);

            _pausedState = new PausedApplicationState();

            _inGameState = new InGameApplicationState(
                viewPortWidth,
                viewPortHeight,
                physicsSimulator,
                processManager,
                actorSpawner);

            _gameOverApplicationState = new GameOverApplicationState();

            _mainMenuApplicationState = new MainMenuApplicationState();
        }

        #endregion

        #region IGameLogic

        public void LoadGame()
        {
            EventManagerSingleton.Instance.TriggerEvent(new MainMenuEvent());
        }

        public void HandleInput()
        {
            _currentState.HandleInput();
        }

        public void Update(GameTime gameTime)
        {
            _currentState.Update(gameTime);
        }

        public void Render()
        {
            _currentState.Render();
        }

        public void RenderDiagnostics(GameTime gameTime)
        {
            _currentState.RenderDiagnostics(gameTime);
        }

        #endregion

        #region Private Methods

        private void HandleMainMenuEvent(ITestudinEvent testudinEvent)
        {
            _mainMenuApplicationState.Load();

            _currentState = _mainMenuApplicationState;
        }

        private void HandleNewGameStarted(ITestudinEvent testudinEvent)
        {
            _inGameState.Load();

            _currentState = _inGameState;
        }

        private void HandlePlayerDiedEvent(ITestudinEvent testudinEvent)
        {
            _gameOverApplicationState.Load();

            _currentState = _gameOverApplicationState;
        }

        private void HandleGamePausedEvent(ITestudinEvent testudinEvent)
        {
            _pausedState.Load();

            _currentState = _pausedState;
        }

        private void HandleGameResumedEvent(ITestudinEvent testudinEvent)
        {
            _currentState = _inGameState;
        }

        #endregion
    }
}
